package com.qclix.inv;

import com.qclix.inv.dao.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;


/**
 * Provides different services for invoice manipulation
 */
public class InvoiceServiceDefaultImpl extends HibernateDaoSupport implements InvoiceService {

    private static final Logger logger = LoggerFactory.getLogger(InvoiceServiceDefaultImpl.class);

    private static final int LAST_INVOICE_TO_RETURN = 9;

    private ClientDAO clientDAO;
    private InvoiceDAO invoiceDAO;
    private TimesheetDAO timesheetDAO;
    private HolidayDAO holidayDAO;
    private ContractDAO contractDAO;

    public void setClientDAO(ClientDAO clientDAO) {
        this.clientDAO = clientDAO;
    }

    public void setInvoiceDAO(InvoiceDAO invoiceDAO) {
        this.invoiceDAO = invoiceDAO;
    }

    public void setTimesheetDAO(TimesheetDAO timesheetDAO) {
        this.timesheetDAO = timesheetDAO;
    }

    public void setHolidayDAO(HolidayDAO holidayDAO) {
        this.holidayDAO = holidayDAO;
    }

    public ContractDAO getContractDAO() {
        return contractDAO;
    }

    public void setContractDAO(ContractDAO contractDAO) {
        this.contractDAO = contractDAO;
    }

    public List<Client> getClients() {
        return clientDAO.getClients();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public Client addClient(Client client) {
        clientDAO.addClient(client);
        return client;
    }

    public boolean hasAtLeastOneClient() {
        return clientDAO.getClientCount() > 0;
    }

    @Transactional(propagation = Propagation.SUPPORTS, isolation = Isolation.SERIALIZABLE)
    public void addInvoice(Invoice invoice) {
        Calendar issueDate = Calendar.getInstance();
        issueDate.setTime(invoice.getIssueDate());
        int year = issueDate.get(Calendar.YEAR);
        long amountOfInvoicesThisYear = 0;//getAmountOfInvoicesInYear(year);

        amountOfInvoicesThisYear = invoiceDAO.getAmountOfInvoicesOnYear(year);
        StringBuilder buffer = new StringBuilder();
        buffer.append(year);
        if (amountOfInvoicesThisYear < 9) {
            buffer.append('0');
        }
        if (amountOfInvoicesThisYear < 99) {
            buffer.append('0');
        }
        if (amountOfInvoicesThisYear < 999) {
            buffer.append('0');
        }
        buffer.append(amountOfInvoicesThisYear + 1);
        invoice.setNumber(buffer.toString());
        if (logger.isTraceEnabled()) {
            logger.trace("new invoice number " + invoice.getNumber() + " generated");
        }
        invoiceDAO.add(invoice);
        if (logger.isTraceEnabled()) {
            if (invoice.getClient() != null) {
                logger.trace("invoice for client " + invoice.getClient().getName() + " (containing " + invoice.getLines() + " lines) stored");
            } else {
                logger.trace("invoice for contract " + invoice.getContract().getReference() + " (containing " + invoice.getLines() + " lines) stored");
            }
        }
    }

    public Client getLastInvoicedClient() {
        Client lastInvoicedClient = invoiceDAO.getLastInvoice().getClient();
        if (logger.isTraceEnabled()) {
            logger.trace("returning last invoiced client: " + lastInvoicedClient.getName());
        }
        return lastInvoicedClient;
    }

    public List<Invoice> getLastInvoices() {
        return getLastInvoices(LAST_INVOICE_TO_RETURN);
    }

    public List<Invoice> getLastInvoices(int lastInvoices) {
        return invoiceDAO.getLastInvoices(lastInvoices);
    }

    public Invoice getInvoice(String number) {
        return invoiceDAO.getInvoice(number);
    }

    public Timesheet getTimesheet(long id) {
        return timesheetDAO.getTimesheet(id);
    }

    public Timesheet getTimesheet(int month, int year) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.MONTH, month);
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.DAY_OF_MONTH, 1);

        Timesheet timesheet = timesheetDAO.getTimesheetStartingAtDay(cal.getTime());
        if (timesheet != null) {
            for (WeekDay day : timesheet.getDays()) {
                day.setPartOfInvoice(true);
            }
        }
        return timesheet;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void saveTimesheet(Timesheet timesheet) {
        timesheetDAO.add(timesheet);
    }

    public List<Holiday> getHolidays() {
        return holidayDAO.getHolidays();
    }

    @Override
    public SortedSet<Calendar> getTimesheetFirstDays() {
        Set<Calendar> unsortedFirstDays = timesheetDAO.getFirstDaysOfTimesheets();
        SortedSet<Calendar> sortedFirstDays = new TreeSet<Calendar>();
        sortedFirstDays.addAll(unsortedFirstDays);
        return sortedFirstDays;
    }


    @Override
    public Contract getContract(String reference) {
        return contractDAO.getContract(reference);
    }

    @Override
    public List<Contract> getAllContracts() {
        return contractDAO.getAllContracts();
    }

    @Override
    public List<Contract> getContracts(Client client) {
        return contractDAO.getClientContracts(client.getId());
    }

    @Override
    public Contract getContract(Invoice invoice) {
        return contractDAO.getInvoiceContract(invoice.getNumber());
    }

    @Override
    public void saveContract(Contract contract) {
        contractDAO.save(contract);
    }

    @Override
    public List<Contract> getActiveContracts(Calendar cal) {
        return contractDAO.getActiveContract(cal);
    }
}
